Lab 1: NumPy exercises¶
This ungraded assignment is composed of 26 NumPy exercises divided in three difficulty levels: beginner, intermediate, and advanced. In order to understand and solve the exercises, and more generally to gain the maximum benefit from this assignment, it is highly recommended that you read Chapter 1 in the course notes available on Blackboard.
IMPORTANT: Please refer to Chapter 1 as you work through this assignment.
Instructions¶
Download a copy of this notebook from Blackboard.
Run
jupyter notebookand open the.ipynbfile.- Keep the notebook inside the folder it was downloaded with. This folder contains all the dependencies needed for the notebook to work properly.
Work alone or with a partner to solve the quizzes.
- You are supposed to fill in or modify the code marked with the comment
# YOUR CODE HERE - You can check your answers by running the tests provided at the end of each quiz
- You are supposed to fill in or modify the code marked with the comment
Contents¶
- Beginner level
| Exercise | Topic |
|---|---|
| Quiz 1.1 | Array creation I |
| Quiz 1.2 | Array creation II |
| Quiz 1.3 | Attribute .shape |
| Quiz 1.4 | Reshaping |
| Quiz 1.5 | Vectorization I |
| Quiz 1.6 | Vectorization II |
| Quiz 1.7 | Keyword axis |
| Quiz 1.8 | Simple indexing |
| Quiz 1.9 | Slicing |
- Intermediate level
| Exercise | Topic |
|---|---|
| Quiz 2.1 | Creation |
| Quiz 2.2 | Slicing I |
| Quiz 2.3 | Slicing II |
| Quiz 2.4 | Reshaping I |
| Quiz 2.5 | Reshaping II |
| Quiz 2.6 | Reshaping III |
| Quiz 2.7 | Indexing |
| Quiz 2.8 | Broadcasting |
| Quiz 2.9 | Scalar product |
- Advanced level
| Exercise | Topic |
|---|---|
| Quiz 3.1 | Checkboard |
| Quiz 3.2 | Column swapping |
| Quiz 3.3 | Find minimum |
| Quiz 3.4 | Sum pairs of cols |
| Quiz 3.5 | Softmax |
| Quiz 3.6 | Linear regression |
| Quiz 3.7 | Pairwise distances |
| Quiz 3.8 | Advanced indexing |
import numpy as np
Part 1. Beginner level¶
vector_zeros = np.zeros(5) # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(vector_zeros, [0, 0, 0, 0, 0])
matrix_ones = np.ones((8,4)) # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(matrix_ones,
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
# This is the array to work with
matrix = np.array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
nrows = matrix.shape[0] # SOLUTION
ncols = matrix.shape[1] # SOLUTION
assert nrows == 4
assert ncols == 3
# This is the array to work with
volume = np.array([[[5,6,3],
[3,2,7],
[3,5,1]],
[[5,6,3],
[3,2,7],
[3,5,1]]])
flat_vector = volume.ravel() # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(flat_vector, [5, 6, 3, 3, 2, 7, 3, 5, 1, 5, 6, 3, 3, 2, 7, 3, 5, 1])
Quiz 1.5¶
Given an array
x, compute the following expression:
Hint:
- denotes the exponential, which can be computed in numpy with the function
np.exp().- denotes a summation, which can be computed in numpy with the function
np.sum().- denotes the logarithm, which can be computed in numpy with the function
np.log().
# This is the array to work with
x = np.array([7.1, -5.7, 13])
log_sum_exp = np.log(np.exp(x).sum()) # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(log_sum_exp, 13.00273570692086)
# This is the vector to work with
vector = np.array([5, -1, 3, 12, 8, 0])
min_value = vector.min() # SOLUTION
max_value = vector.max() # SOLUTION
print("min:", min_value)
print("max:", max_value)
min: -1 max: 12
assert min_value == -1
assert max_value == 12
np.random.seed(1)
# This is the matrix to work with
matrix = np.random.randint(10,size=(3,4))
mean_all = matrix.mean() # SOLUTION
mean_rows = matrix.mean(axis=1) # SOLUTION
mean_cols = matrix.mean(axis=0) # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(mean_all, 4.66666666666666)
assert_almost_equal(mean_rows, [6.75, 2.0, 5.25])
assert_almost_equal(mean_cols, [3.66666667, 5.66666667, 4., 5.33333333])
Quiz 1.8¶
Swap (in-place) the 2nd and 4th element of a vector.
Remark: Single indexing extracts one element from an array. In Python, single values are always copied when assigned to a variable.
# This is the vector to work with
vector = np.array([5, -1, 3, 12, 8, 0])
print("--- before swap ---")
print(vector)
# YOUR CODE HERE
None
# BEGIN SOLUTION
vector[1], vector[3] = vector[3], vector[1]
# END SOLUTION
print("\n--- after swap ---")
print(vector)
--- before swap --- [ 5 -1 3 12 8 0] --- after swap --- [ 5 12 3 -1 8 0]
from numpy.testing import assert_almost_equal
assert_almost_equal(vector, [5, 12, 3, -1, 8, 0])
Quiz 1.9¶
Given a matrix, extract the elements indicated in the figure below.
Hint: Recall that you can extract a contiguous part of a matrix via the slicing notation:
matrix[start_row : stop_row : step_row , start_col : stop_col : step_col]You can omit any of the slicing indices. For example, these are valid slicing:
start:stop,start:,:stop,:,::step.
# This is the matrix to work with
matrix = np.array([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12]])
A = matrix[0] # SOLUTION
B = matrix[:,0] # SOLUTION
C = matrix[2:,1:] # SOLUTION
D = matrix[::2] # SOLUTION
E = matrix[0:2,0:2] # SOLUTION
F = matrix[:,::2] # SOLUTION
G = matrix[1::2] # SOLUTION
H = matrix[:2,1:] # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(A, [1, 2, 3])
assert_almost_equal(B, [ 1, 4, 7, 10])
assert_almost_equal(C, [[8, 9], [11, 12]])
assert_almost_equal(D, [[1, 2, 3], [ 7, 8, 9]])
assert_almost_equal(E, [[1, 2], [4, 5]])
assert_almost_equal(F, [[ 1, 3], [ 4, 6], [ 7, 9], [10, 12]])
assert_almost_equal(G, [[ 4, 5, 6], [10, 11, 12]])
assert_almost_equal(H, [[2, 3], [5, 6]])
Part 2. Intermediate level¶
Quiz 2.1¶
Create a vector of size 10 filled with zero, and set the first and last elements to
1.
Remark: The solution can take more than one line of code.
# ADD CODE HERE
head_tail = None
# BEGIN SOLUTION
head_tail = np.zeros(10)
head_tail[0] = 1
head_tail[-1] = 1
# END SOLUTION
print(head_tail)
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
from numpy.testing import assert_almost_equal
assert_almost_equal(head_tail, [1, 0, 0, 0, 0, 0, 0, 0, 0, 1])
Quiz 2.2¶
Create a vector of size 20 with an alternating pattern of
0and1.
Expected output:
[0, 1, 0, 1, 0, 1, ..., 0, 1]
# ADD CODE HERE
zero_one = None
# BEGIN SOLUTION
zero_one = np.zeros(20)
zero_one[1::2] = 1
# END SOLUTION
print(zero_one)
[0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1.]
from numpy.testing import assert_almost_equal
assert_almost_equal(zero_one, [0,1, 0,1, 0,1, 0,1, 0,1, 0,1, 0,1, 0,1, 0,1, 0,1])
Quiz 2.3¶
Swap the first half of a vector with its last half.
Example:
[1,2,3, 4,5,6] --> [4,5,6, 1,2,3]
Hints:
- The size of a vector is given by the attribute
.sizeor.shape[0].- Use the integer division
n//2to compute the length of half a vector.- The function
np.concatenate()merges a copy of the input vectors into a new array.
# This is the vector to work with
vector = np.array([4,2,8, 7,1,3])
# ADD CODE HERE
swapped = None
# BEGIN SOLUTION
n = vector.size
head = vector[:n//2]
tail = vector[n//2:]
swapped = np.concatenate([tail,head])
# END SOLUTION
print("vector:", vector)
print("swapped:", swapped)
vector: [4 2 8 7 1 3] swapped: [7 1 3 4 2 8]
from numpy.testing import assert_almost_equal
assert_almost_equal(vector, [4, 2, 8, 7, 1, 3])
assert_almost_equal(swapped, [7, 1, 3, 4, 2, 8])
Quiz 2.4¶
Reshape an array of
nelements into a matrix withn/2rows and2columns.
Hint:
.reshape()
# This is the array to work with
a = np.array([[5,6,3], [3,2,7], [4,2,8], [7,1,3]])
reshaped = a.reshape(a.size//2, 2) # SOLUTION
print("--- before ---")
print(a)
print("--- after ---")
print(reshaped)
--- before --- [[5 6 3] [3 2 7] [4 2 8] [7 1 3]] --- after --- [[5 6] [3 3] [2 7] [4 2] [8 7] [1 3]]
from numpy.testing import assert_almost_equal
assert_almost_equal(reshaped, [[5, 6], [3, 3], [2, 7], [4, 2], [8, 7], [1, 3]])
Quiz 2.5¶
Given a vector with an even number of elements, compute the sum of two consecutive elements.
- Expected result: Vector holding the sum of 1st and 2nd elements, then the sum of 3rd and 4th elements, and so on.
- Example: [1, 2, 3, 4, 5, 6] --> [3, 7, 11]
Hint: Reshape the vector into a matrix with n/2 rows and 2 columns.
np.random.seed(6)
# This is the vector to work with
a = np.random.randint(10,size=(6,))
sum_pairs = a.reshape(a.size//2, 2).sum(axis=1) # SOLUTION
print("vector:", a)
print("sum by pairs:", sum_pairs)
vector: [9 3 4 0 9 1] sum by pairs: [12 4 10]
from numpy.testing import assert_almost_equal
assert_almost_equal(sum_pairs, [12, 4, 10])
Quiz 2.6¶
Given a matrix with an even number of rows, compute the sum of two consecutive rows.
- Expected result: Vector holding the sum of elements on 1st and 2nd rows, the sum of elements on 3rd and 4th rows, and so on.
Hint: Reshape the matrix so as to align the rows that must be summed together.
np.random.seed(1)
# This is the matrix to work with
A = np.random.randint(10,size=(6,4))
sum_rows = A.reshape(-1,2*A.shape[1]).sum(axis=1) # SOLUTION
print("---- A ----")
print(A)
print()
print("Sum:", sum_rows)
---- A ---- [[5 8 9 5] [0 0 1 7] [6 9 2 4] [5 2 4 2] [4 7 7 9] [1 7 0 6]] Sum: [35 34 41]
from numpy.testing import assert_almost_equal
assert_almost_equal(sum_rows, [35, 34, 41])
np.random.seed(1)
# This is the matrix to work with
matrix = np.random.randint(50, size=(4,6))
print("--- before ---")
print(matrix)
# ADD CODE HERE
None
# BEGIN SOLUTION
i = matrix.argmin()
matrix.flat[i] = -10
# END SOLUTION
print()
print("----- after -----")
print(matrix)
--- before --- [[37 43 12 8 9 11] [ 5 15 0 16 1 12] [ 7 45 6 25 20 37] [18 20 11 42 28 29]] ----- after ----- [[ 37 43 12 8 9 11] [ 5 15 -10 16 1 12] [ 7 45 6 25 20 37] [ 18 20 11 42 28 29]]
from numpy.testing import assert_almost_equal
assert_almost_equal(matrix,
[[ 37, 43, 12, 8, 9, 11],
[ 5, 15, -10, 16, 1, 12],
[ 7, 45, 6, 25, 20, 37],
[ 18, 20, 11, 42, 28, 29]])
np.random.seed(1)
# This is the matrix to work with
matrix = np.random.randint(10, size=(3,6))
# STEP 1. Compute the max along the rows
max_values = matrix.max(axis=1, keepdims=True) # SOLUTION
# STEP 2. Divide the matrix by the max values
new_matrix = matrix / max_values # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(max_values, [[9], [9], [7]])
assert_almost_equal(new_matrix,
[[0.55555556, 0.88888889, 1. , 0.55555556, 0. , 0. ],
[0.11111111, 0.77777778, 0.66666667, 1. , 0.22222222, 0.44444444],
[0.71428571, 0.28571429, 0.57142857, 0.28571429, 0.57142857, 1. ]])
Quiz 2.9¶
Compute the following expression:
where
with the convention .
Hint: Note that the vectors don't have the same size:
w- vector of shape(Q+1,)x- vector of shape(Q,).To get around this, you can proceed as follows:
- Slice
wso as to extract the elementsw[1], ..., w[Q].- Compute the scalar product between
xand the above elements.- Add
w[0]to the final result.
# These are the vectors to work with
x = np.array([3, 2, 7])
w = np.array([-3, 2, 0.5, -1])
# STEP 1. Compute 'w0 + w1*x1 + ... + wQ*xQ'.
w_dot_x = w[0] + np.dot(x, w[1:]) # SOLUTION
# STEP 2. Compute the sigmoid of 'x_dot_w', as defined above.
logistic = 1/(1+np.exp(-w_dot_x)) # SOLUTION
from numpy.testing import assert_almost_equal
assert_almost_equal(logistic, 0.04742587317756678)
Part 3. Advanced level (optional)¶
Quiz 3.1¶
Create a
8x8matrix and fill it with a checkerboard pattern.
Expected output:
[[0 1 0 1 0 1 0 1] [1 0 1 0 1 0 1 0] [0 1 0 1 0 1 0 1] [1 0 1 0 1 0 1 0] [0 1 0 1 0 1 0 1] [1 0 1 0 1 0 1 0] [0 1 0 1 0 1 0 1] [1 0 1 0 1 0 1 0]]
# ADD CODE HERE
checkboard = None
# BEGIN SOLUTION
checkboard = np.zeros((8,8))
checkboard[1::2,::2] = 1
checkboard[::2,1::2] = 1
# END SOLUTION
checkboard
array([[0., 1., 0., 1., 0., 1., 0., 1.],
[1., 0., 1., 0., 1., 0., 1., 0.],
[0., 1., 0., 1., 0., 1., 0., 1.],
[1., 0., 1., 0., 1., 0., 1., 0.],
[0., 1., 0., 1., 0., 1., 0., 1.],
[1., 0., 1., 0., 1., 0., 1., 0.],
[0., 1., 0., 1., 0., 1., 0., 1.],
[1., 0., 1., 0., 1., 0., 1., 0.]])
from numpy.testing import assert_almost_equal
assert_almost_equal(checkboard,
[[0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 0]])
Quiz 3.2¶
Swap (in-place) the 2nd and 4th column of a matrix.
Recall when data is shared or copied:
- No copy - A simple assignment produces an alias of the original array.
- Shared data - Slicing creates a view of the original array.
- Copied data - The method
.copy()makes a copy of the original array.
# This is the matrix to work with
matrix = np.arange(15).reshape(3,5)
print("--- matrix (before swap) ---")
print(matrix)
# ADD CODE HERE
# BEGIN SOLUTION
temp = matrix[:,1].copy()
matrix[:,1] = matrix[:,3]
matrix[:,3] = temp
# END SOLUTION
print("\n--- matrix (after swap) ---")
print(matrix)
--- matrix (before swap) --- [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] --- matrix (after swap) --- [[ 0 3 2 1 4] [ 5 8 7 6 9] [10 13 12 11 14]]
from numpy.testing import assert_almost_equal
assert_almost_equal(matrix,
[[ 0, 3, 2, 1, 4],
[ 5, 8, 7, 6, 9],
[10, 13, 12, 11, 14]])
Quiz 3.3¶
Find the element in a vector nearest to the value
1.
Hint: Given the vector
x, compute the absolute values ofx-1, and then find the indexiof the minimum element.
Example: step-by-step
- Consider the vector
x = [5, -1, 3, 1.2, 8, 0]. What is the element inxnearest to the value1?- The operation
x-1results in the vector[4, -2, 2, 0.2, 7, -1].- The absolute value of
x-1is thus[4, 2, 2, 0.2, 7, 1].- What is the minimum element of this vector? Take notice of its position. It is the same as the element you are looking for!
# This is the vector to work with
vector = np.array([5, -1, 3, 1.2, 8, 0])
# ADD CODE HERE
nearest_value = None
# BEGIN SOLUTION
abs_diff = np.abs(vector-1)
i = abs_diff.argmin()
nearest_value = vector[i]
# END SOLUTION
print('nearest value:', nearest_value)
nearest value: 1.2
assert nearest_value == 1.2
Quiz 3.4¶
Given a matrix with an even number of columns, compute the sum of two consecutive columns.
- Expected result: Vector holding the sum of elements on 1st and 2nd columns, the sum of elements on 3rd and 4th columns, and so on.
Hint: Recall that a matrix is stored in row-major order, so you need to come up with a creative way of reshaping the matrix.
np.random.seed(1)
# This is the matrix to work with
A = np.random.randint(10,size=(6,4))
sum_cols = A.sum(axis=0).reshape(-1,2).sum(axis=1) # SOLUTION
print("--- A ---")
print(A)
print()
print("Sum:", sum_cols)
--- A --- [[5 8 9 5] [0 0 1 7] [6 9 2 4] [5 2 4 2] [4 7 7 9] [1 7 0 6]] Sum: [54 56]
from numpy.testing import assert_almost_equal
assert_almost_equal(sum_cols, [54, 56])
Quiz 3.5¶
axis=None- sum over all the elementsaxis=0- sum over the columnsaxis=1- sum over the rows.
Implement a function that computes the following expression:
When the input is a matrix, the function must operate according to an input parameter
axis:
Hint: In the skeleton function given below, print the shapes of intermediate variables
x_exp,x_sumandsto help you with broadcasting.
The shape of
x_summust be(1,1),(1,3)or(2,1)depending on the input parameteraxis.The shape of
x_expandsmust always be(2,3).Then, the division
x_exp/x_sumwill work due to broadcasting.
def softmax(x, axis=None):
"""Compute the softmax of x"""
# Compute the exponential.
x_exp = np.exp(x) # SOLUTION
# Sum the elements along the specified axis. Don't forget to keep the dimensions!
x_sum = np.sum(x_exp, axis=axis, keepdims=True) # SOLUTION
# Compute the division. It should automatically use broadcasting!
s = x_exp / x_sum # SOLUTION
return s
x = np.array([[9, 2, 5],
[7, 5, 0]])
s_all = softmax(x)
s_cols = softmax(x, axis=0)
s_rows = softmax(x, axis=1)
from numpy.testing import assert_almost_equal
assert_almost_equal(s_all,
[[8.52513572e-01, 7.77391752e-04, 1.56143307e-02],
[1.15375166e-01, 1.56143307e-02, 1.05208533e-04]])
assert_almost_equal(s_cols,
[[0.88079708, 0.04742587, 0.99330715],
[0.11920292, 0.95257413, 0.00669285]])
assert_almost_equal(s_rows,
[[9.81135202e-01, 8.94679497e-04, 1.79701181e-02],
[8.80090205e-01, 1.19107257e-01, 8.02538386e-04]])
Quiz 3.6¶
Implement a function that computes the following expression:
where
with the convention .
Hint: This computation can be vectorized as follows.
- The vectors are stacked in a matrix , whereas the scalars are stacked in a vector :
- The products , for every index , are computed by multiplying by :
- The distance is now equal to the squared norm of the difference between and :
Note hovewer that the shapes of
Xandwdon't align for a matrix-vector product. To get around this, you can proceed as follows:
- Slice
wso as to extract the elementsw[1], ..., w[Q].- Compute the product between
Xand the above elements.- Add
w[0]to the final result.
def ssd_cost(w, X, y):
"""Calculates the sum of squared differences.
Arguments:
w -- flat vector of shape (Q+1,)
X -- matrix of shape (N, Q)
y -- flat vector of shape (N,)
Returns:
s -- Scalar
"""
# Compute w0 + w1*X1 + ... + wQ*XQ
w_dot_x = w[0] + np.dot(X, w[1:]) # SOLUTION
# Compute the sum of squared distances between x_dot_w and y
J = np.sum( (y - w_dot_x)**2 ) # SOLUTION
return J
w = np.array([-3, 2, 0.5, -1])
X = np.array([[4, -5, 2],
[5, 3, -3],
[5, 2, 1],
[8, 6, 9],
[5, -6, 2]])
y = np.array([6, -3, 0.5, 1, 0.])
assert ssd_cost(w, X, y) == 322.75
Quiz 3.7¶
Compute the pairwise distance between the rows of two matrices.
Hint: It is possible to compute the differences
X[i] - Y[j]via broadcasting. Here is how to do so.
Reshape the matrix
Xto size(N,1,M).Reshape the matrix
Yto size(1,K,M).The reshaped arrays now align for broadcasting.
Remember: You can use the
Noneoperator to insert an axis of length 1.
The resulting array
Dis such thatD[i,j]holds the vectorX[i] - Y[j].You can use the function
np.linalg.norm(..., axis=...)to compute the norm along a given axis.
def distance_matrix(X, Y):
"""Compute the distance between X[i] and Y[j] for all i,j"""
# Reshape 'X' to the appropriate shape
X_3D = X[:,None] # SOLUTION
# Reshape 'Y' to the appropriate shape
Y_3D = Y[None,:] # SOLUTION
# Subtract the matrices. It should automatically use broadcasting.
diff = X_3D - Y_3D # SOLUTION
# Compute the norm along the right axis. You should get a matrix of shape (X.shape[0], Y.shape[0])
dist = np.linalg.norm(diff, axis=2) # SOLUTION
return dist
np.random.seed(24)
X = np.random.randint(20,size=(4,5))
Y = np.random.randint(20,size=(3,5))
dist = distance_matrix(X, Y)
from numpy.testing import assert_almost_equal
assert_almost_equal(dist,
[[19.54482029, 11.48912529, 24.85960579],
[23.87467277, 21.21320344, 18.92088793],
[23.47338919, 24.75883681, 27.03701167],
[18.24828759, 16.21727474, 12.36931688]])
Quiz 3.8¶
The Game of Life is a grid of square cells that can be in one of two possible states: live or dead.
The player creates an initial configuration of live cells and observes how they evolve. Every cell interacts with its 8 neighbours (the cells horizontally, vertically, or diagonally adjacent). At each step in time, the following transitions occur.
Underpopulation: Any live cell with fewer than two live neighbours dies.
Overcrowding: Any live cell with more than three live neighbours dies.
Stability: Any live cell with two or three live neighbours lives unchanged to the next generation.
Rebirth: Any dead cell with exactly three live neighbours becomes a live cell.
The initial pattern constitutes the 'seed' of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed. Births and deaths happen simultaneously. The rules continue to be applied repeatedly to create further generations.
The Game of Life can be implemented by a matrix whose elements represent the single cells:
0means the cell is dead,1means the cell is alive.
board = [[0,0,0,0,0,0],
[0,0,0,1,0,0],
[0,1,0,1,0,0],
[0,0,1,1,0,0],
[0,0,0,0,0,0],
[0,0,0,0,0,0]]
The cells evolve through a for-loop which involves two operations at each iteration:
- counting the neighbors,
- applying the evolution rules.
Here is how the main function looks like.
def game_of_life(board, steps):
for i in range(steps):
count = neighbor_counting(board)
board = evolution_rules(board, count)
return board
We provide you with the function neighbor_counting. Your job is to implement evolution_rules.
def neighbor_counting(board):
padded = np.pad(board, (1,1), 'constant')
count = padded[ :-2, :-2] + padded[:-2, 1:-1] + padded[ :-2, 2:] \
+ padded[1:-1, :-2] + + padded[1:-1, 2:] \
+ padded[2: , :-2] + padded[2: , 1:-1] + padded[2: , 2:]
return count
Implement a function that applies the evolution rules of the Game of Life (see the description above).
Hint: Use advanced indexing to avoid loops!
- Translate the evolution rules to boolean conditions,
- Use the boolean masks to set the new values.
def evolution_rules(board, count):
"""
Arguments:
board -- matrix whose elements indicate whether a cell is dead (0) or alive (1)
count -- matrix whose elements indicate the number of alive neighbors of a cell
Returns:
new_board -- matrix with the same shape as 'board'
"""
new_board = np.zeros_like(board)
# Underpopulation
R1 = (board == 1) & (count < 2) # SOLUTION
new_board[R1] = 0 # SOLUTION
# overcrowding
R2 = (board == 1) & (count > 3) # SOLUTION
new_board[R2] = 0 # SOLUTION
# stability
R3 = (board == 1) &((count == 2) | (count == 3)) # SOLUTION
new_board[R3] = board[R3] # SOLUTION
# rebirth
R4 = (board == 0) & (count == 3) # SOLUTION
new_board[R4] = 1 # SOLUTION
return new_board
board = np.array([[0,0,0,0,0,0],
[0,0,0,1,0,0],
[0,1,0,1,0,0],
[0,0,1,1,0,0],
[0,0,0,0,0,0],
[0,0,0,0,0,0]])
count = neighbor_counting(board)
new_board = evolution_rules(board, count)
from numpy.testing import assert_almost_equal
assert_almost_equal(new_board,
[[0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0],
[0, 0, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
Bonus :-)¶
Congratulations! You made it to the end. As a reward, sit back and watch The Game of Life.
Run the following cells to play the animation. The creation process may take a while.
def game_of_life(board, steps):
history = [board]
for i in range(steps):
count = neighbor_counting(board)
board = evolution_rules(board, count)
history.append(board)
return history
def create_animation(history, speed=1):
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure(figsize=(7,7))
im = plt.imshow(history[0], cmap=plt.cm.seismic, interpolation='bicubic')
plt.axis('off')
plt.close(fig)
def animate(i):
im.set_data(history[i])
#im.autoscale()
return (im,)
anim = animation.FuncAnimation(fig, animate, frames=range(0,len(history),speed), interval=100, blit=True)
return anim
glider_gun =\
[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
seed = np.zeros((50, 70))
seed[1:10,1:37] = glider_gun
history = game_of_life(seed, steps=500)
from IPython.display import HTML
animation = create_animation(history, speed=2)
HTML(animation.to_jshtml())